
package org.sopt.dev.ImgKnight.agent.HttpHandler;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;

import net.htmlparser.jericho.Config;
import net.htmlparser.jericho.Element;
import net.htmlparser.jericho.LoggerProvider;
import net.htmlparser.jericho.Segment;
import net.htmlparser.jericho.Source;
import net.htmlparser.jericho.StartTag;
import net.htmlparser.jericho.StreamedSource;
import net.htmlparser.jericho.Tag;

import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpConnectionManager;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.HttpVersion;
import org.apache.commons.httpclient.SimpleHttpConnectionManager;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.HeadMethod;
import org.apache.commons.httpclient.params.HttpConnectionManagerParams;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.log4j.Logger;

import org.sopt.dev.ImgKnight.agent.Agent;

public class HttpHandler {

	private Agent agent = null;
	
	private Logger exceptionLogger = null;
	
	public HttpHandler() {
		agent = Agent.getInstance();
		Config.LoggerProvider = LoggerProvider.DISABLED;
		exceptionLogger = Logger.getLogger("agent.exception");
	}
	
	private HttpClient makeHttpClient(int connTimeout, int soTimeout) {
		HttpConnectionManager connMgr = new SimpleHttpConnectionManager();
		HttpConnectionManagerParams httpConnMgrParams = new HttpConnectionManagerParams();
		httpConnMgrParams.setConnectionTimeout(connTimeout);
		httpConnMgrParams.setSoTimeout(soTimeout);
		connMgr.setParams(httpConnMgrParams);
		connMgr.closeIdleConnections(1000);
		return new HttpClient(connMgr);
	}
	
	public GetMethod getHttpGetMethod(URL url) {
		HttpClient hc = makeHttpClient(agent.getAgentConfig().HttpGetTimeout, 
				agent.getAgentConfig().HttpGetSoTimeout);
		hc.getParams().setParameter("http.protocol.version", HttpVersion.HTTP_1_0);
		GetMethod  getMethod = new GetMethod(url.toString());
		getMethod.setRequestHeader("User-Agent", agent.getAgentConfig().AgentName);
		getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, 
				new DefaultHttpMethodRetryHandler(3, false));
		try {
			int statusCode = hc.executeMethod(getMethod);
			if( statusCode == HttpStatus.SC_OK )
				return getMethod;
		} catch(HttpException e) {
			exceptionLogger.warn("Fatal protocol violation: "+e+": "+getMethod.getStatusLine()+", host: "+url);
		} catch(IOException e) {
			exceptionLogger.warn("Fatal transport error: "+e+": "+getMethod.getStatusLine()+", host: "+url);
		}
		return null;
	}
	
	public HeadMethod getHttpHeadMethod(URL url) {
		HttpClient hc = makeHttpClient(agent.getAgentConfig().HttpHeadTimeout,
				agent.getAgentConfig().HttpHeadSoTimeout);
		hc.getParams().setParameter("http.protocol.version", HttpVersion.HTTP_1_0);
		HeadMethod hm = new HeadMethod(url.toString());
		hm.setRequestHeader("User-Agent", agent.getAgentConfig().AgentName);
		hm.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, 
				new DefaultHttpMethodRetryHandler(3, false));
		try {
			int statusCode = hc.executeMethod(hm);
			if( statusCode == HttpStatus.SC_OK )
				return hm;
		} catch(HttpException e) {
			exceptionLogger.warn("Fatal protocol violation: "+e+": "+hm.getStatusLine()+", host: "+url);
		} catch(IOException e) {
			exceptionLogger.warn("Fatal transport error: "+e+": "+hm.getStatusLine()+", host: "+url);
		}
		return null;
	}
	
	public HashMap<String, List<String>> 
	getHttpElementsMapFromStreamBySource(BufferedInputStream responseBodyStream, Vector<String> tagNames) {
		HashMap<String, List<String>> elementMap = new HashMap<String, List<String>>();
		for(String tagName : tagNames)
			elementMap.put(tagName, new LinkedList<String>());
		try {
			Source s = new Source(responseBodyStream);
			s.fullSequentialParse();
			List<Element> elementList = null;	
			for(String tag : tagNames) {
				elementList = s.getAllElements(tag);
				for(Element e : elementList)
					elementMap.get(tag).add(e.toString());
			}
		} catch (Exception e) {
			exceptionLogger.warn("Unexpected exception: "+e);
		}
		return elementMap;
	}
	
	public HashMap<String, List<String>> 
	getHttpElementsMapFromStreamByStreamedSource(BufferedInputStream responseBodyStream, Vector<String> tagNames) {
		HashMap<String, List<String>> elementMap = new HashMap<String, List<String>>();
		for(String tagName : tagNames)
			elementMap.put(tagName, new LinkedList<String>());
		StreamedSource s = null;
		try {
			s = new StreamedSource(responseBodyStream);
			for(Segment segment : s) {
				if( segment instanceof Tag ) {
					Tag tag = (Tag)segment;
					if( tagNames.contains(tag.getName()) && tag instanceof StartTag )
						elementMap.get(tag.getName()).add(tag.toString());
				}
			}
		} catch (Exception e) {
			exceptionLogger.warn("Unexpected exception: "+e);
		} finally {
			if( s != null )
				try { s.close(); } catch (IOException ignore) {}
		}
		return elementMap;
	}
	
}
